package com.roadmap.biz.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;

import com.roadmap.biz.message.NoticeManager;
import com.roadmap.common.BaseAuthEntity;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Aspirant;
import com.roadmap.entities.LimitedAccess;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidStatusException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotAuthorizedException;
import com.roadmap.exception.NotMatchedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.DatabaseUtility;
import com.roadmap.repo.db.IDatabaseUtility;
import com.roadmap.repo.db.RdcQuery;

public abstract class BaseManager implements IManager {
	protected final Logger _log = RdcLogger.getLogger(this.getClass());
	protected final IDatabaseUtility _dbUtil = new DatabaseUtility();
	// 通知管理器
	protected final NoticeManager _noticeManager;
	// 当前用户
	private String _currentUserId;
	// 访问项所有者
	private String _itemOwnerId;
	// 访问项类型
	private final Class _managedEntityClass;
	// 访问项的上级对象类型
	private final Class _parentClass;
	// 查询结果的最大数目
	private int _maxResultCount;
	// 显示指定页面数的结果
	private int _page = 1;

	public BaseManager(String currentUser, String itemOwner, Class entityClass) {
		this._currentUserId = currentUser;
		this._itemOwnerId = itemOwner;
		this._managedEntityClass = entityClass;
		this._parentClass = Aspirant.class;
		this._noticeManager = new NoticeManager(currentUser, _dbUtil);
	}

	public BaseManager(String currentUser, String itemOwner, Class entityClass,
			Class parentClass) {
		this._currentUserId = currentUser;
		this._itemOwnerId = itemOwner;
		this._managedEntityClass = entityClass;
		this._parentClass = parentClass;
		this._noticeManager = new NoticeManager(currentUser, _dbUtil);
	}

	protected final Class getParentClass() {
		return this._parentClass;
	}

	public final IDatabaseUtility getDbUtil() {
		return this._dbUtil;
	}

	/** 返回访问项所有者ID */
	public final String getOwnerId() {
		return this._itemOwnerId;
	}

	/** 返回当前用户ID */
	public final String getCurrentUserId() {
		return this._currentUserId;
	}

	/** 返回可用的通知器 */
	public final NoticeManager getNoticeManager() {
		return this._noticeManager;
	}

	public final int getMaxResultCount() {
		return this._maxResultCount;
	}

	public int getPage() {
		return this._page;
	}

	public void setPage(int page) {
		this._page = page;
	}

	public BaseEntity cancel(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		throw new ActionNotSupportedException();
	}

	public BaseEntity complete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException, InvalidStatusException {
		throw new ActionNotSupportedException();
	}

	public void create(BaseEntity entity) throws NullObjectException,
			InvalidValueException, ServerException, InvalidActionException,
			EntityNotFoundException, ActionNotSupportedException,
			NotAuthorizedException, InvalidStatusException {
		checkEntityIsValid(entity);
		_dbUtil.save(entity);
		_noticeManager.notifyForEntry(entity, ActionType.CREATE, null);

	}

	public void delete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, InvalidActionException,
			EntityNotFoundException, NotAuthorizedException,
			ActionNotSupportedException {
		BaseEntity entry = get(entryId);
		if (entry != null) {
			_dbUtil.delete(entry);
		}
		else {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error("the specified entity was not found.", e);
			throw e;
		}
	}

	public void edit(BaseEntity entry) throws NullObjectException,
			InvalidValueException, ServerException, InvalidActionException,
			NotMatchedException {
		checkEntityIsValid(entry);

		if (!(entry instanceof BaseAuthEntity)) {
			_dbUtil.update(entry);
			return;
		}

		BaseAuthEntity authEntry = (BaseAuthEntity) entry;
		if (authEntry.getVisibility() != BaseConst.ENTRY_VISIBILITY_LIMITED) {
			_dbUtil.update(entry);
			return;
		}

		LimitedAccess access = (LimitedAccess) _dbUtil.getEntityById(
				LimitedAccess.class, authEntry.getId());
		if (access == null) {
			NotMatchedException e = new NotMatchedException();
			_log.error("The limited entity has no access record", e);
			throw e;
		}
		access.setMemberIDs(authEntry.getLimitedMembers());

		Vector<BaseEntity> entities = new Vector<BaseEntity>();
		entities.add(entry);
		entities.add(access);
		_dbUtil.saveOrUpdateEntities(entities);
	}

	public BaseEntity get(String entryId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException, EntityNotFoundException {
		if(entryId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("Comment id is null.", e);
			throw e;
		}
		return _dbUtil.getEntityById(_managedEntityClass, entryId);
	}

	public List getEntitiesOfOwner() throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException,
			InvalidQueryException {
		String hql = String.format(
				"from %s where aspirantId=? order by createdTime DESC",
				this._managedEntityClass.getSimpleName());
		Object[] params = { this._itemOwnerId };
		List results = getDbUtil().executeHQLQuery(hql, params);
		return getResultsForSpecifiedPage(results);
	}

	public List getEntitiesOfParent(String parentId) throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException,
			InvalidQueryException, EntityNotFoundException,
			NotAuthorizedException {
		return getEntitiesOfOwner();
	}

	public BaseEntity getEntityExt(String entryId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException,
			EntityNotFoundException, NotMatchedException,
			NotAuthorizedException, ActionNotSupportedException {
		return null;
	}

	public List getExecutableEntities() throws HibernateException,
			ServerException, InvalidQueryException, InvalidValueException,
			NotMatchedException, ActionNotSupportedException {
		return null;
	}

	/** 检查传入的实体参数是否合法 */
	final protected void checkEntityIsValid(BaseEntity entity)
			throws InvalidValueException {
		if (!_managedEntityClass.isInstance(entity)) {
			InvalidValueException ive = new InvalidValueException();
			String msg = String.format("The entity is not an instance of %s",
					_managedEntityClass.getName());
			_log.error(msg, ive);
			throw ive;
		}
	}

	/**
	 * 检查当前用户是否有权限访问指定项
	 */
	final protected boolean checkUserAccess(BaseEntity entry)
			throws InvalidValueException, ServerException, NotMatchedException,
			InvalidQueryException {
		if (entry == null) {
			NullObjectException e = new NullObjectException();
			_log.error("Entered entry is NULL.", e);
			throw e;
		}

		// 非LimitedBaseEntity对象没有权限属性
		if (!(entry instanceof BaseAuthEntity)) {
			return true;
		}

		// 获得访问项的权限值
		int visibility = ((BaseAuthEntity) entry).getVisibility();
		switch (visibility) {
		case BaseConst.ENTRY_VISIBILITY_PRIVATE:
			// 当访问项权限是private时，只对item拥有者可见
			if (getCurrentUserId().equals(getOwnerId())) {
				return true;
			}
			break;
		case BaseConst.ENTRY_VISIBILITY_LIMITED:
			// 为限制级时，查询权限设置
			LimitedAccess access = (LimitedAccess) getDbUtil().getEntityById(
					LimitedAccess.class, entry.getId());
			if (access != null && access.getMemberIDs() != null) {
				if (access.getMemberIDs().contains(getCurrentUserId())) {
					return true;
				}
			}
			else {
				// 如果是limited级，数据库里必须有相关记录
				NotMatchedException e = new NotMatchedException();
				_log
						.error(
								"The entity is limited but relating record was not found.",
								e);
				throw e;
			}
			break;
		case BaseConst.ENTRY_VISIBILITY_PROTECTED:
			// 好友级别的权限检查，在Action中进行，因此此处返回true
			return true;
		default:
			InvalidValueException e = new InvalidValueException();
			_log.error("Invalid access value: " + visibility, e);
			throw e;
		}

		return false;
	}

	/** 检查当前用户对容器中limited项的访问权限，如果对某些项没有权限，则从map容器中移除 */
	final protected void filterLimitedEntities(String[] entryIDs,
			Map limitedEntryMap) throws NullObjectException, ServerException,
			NotMatchedException {
		if (entryIDs == null || limitedEntryMap == null) {
			NullObjectException e = new NullObjectException();
			_log.error(e);
			throw e;
		}
		else if (entryIDs.length == 0 || limitedEntryMap.isEmpty()) {
			return;
		}

		List results = getDbUtil().executeHQLQeury(
				RdcQueryConst.HQL_LIMITEDACCESSES_BY_IDS, "ids", entryIDs);
		if (results == null || results.isEmpty()) {
			NotMatchedException e = new NotMatchedException();
			_log.error("Limited tasks have no access records in database.", e);
			throw e;
		}

		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			LimitedAccess access = (LimitedAccess) iter.next();
			// 如果当前用户对limited任务没有访问权限，则将任务从容器里删除
			if (access.getMemberIDs() == null
					|| !access.getMemberIDs().contains(getCurrentUserId())) {
				limitedEntryMap.remove(access.getId());
			}
		}
	}

	/**
	 * 检查当前用户是否有权限执行创建、更新和删除等操作
	 * 
	 * @throws NotAuthorizedException:
	 *             当前用户没有执行操作的权限
	 */
	final protected void checkUserCUD(String entityOwnerId)
			throws NotAuthorizedException {
		if (!getCurrentUserId().equals(entityOwnerId)) {
			NotAuthorizedException e = new NotAuthorizedException();
			String msg = "The current user is not allowed to perform the action.";
			_log.error(msg, e);
			throw e;
		}
	}

	/** 该方法只用于查询有权限属性的实体如目标、计划、任务。结果集里的对象只能是BaseEntity */
	final protected List getEntities(String hql, Object[] params)
			throws ServerException, NotMatchedException, InvalidValueException {
		List list = getDbUtil().executeHQLQuery(hql, params);
		return getAuthorizedEntities(list);
	}

	final protected List getAuthorizedEntities(List list)
			throws NullObjectException, ServerException, NotMatchedException {
		if (list == null || list.isEmpty()) {
			return null;
		}

		// 判断结果集里的对是否是BaseAuthEntity。如果不是，直接处理结果集，否则将检查当前用户是否能访问每个对象
		if (!(list.get(0) instanceof BaseAuthEntity)) {
			return getResultsForSpecifiedPage(list);
		}

		// 存放当前用户可访问的项
		Map<String, BaseEntity> entityMap = new LinkedHashMap<String, BaseEntity>();

		// 存放权限为limited的任务的ID
		List<String> limitedTaskIDs = new ArrayList<String>();

		// 存放当前用户可访问的项
		Iterator iter = list.iterator();
		while (iter.hasNext()) {
			BaseAuthEntity entity = (BaseAuthEntity) iter.next();

			// 如果访问项权限不是private或当前用户即访问项拥有者，则将访问项放入容器
			if (entity.getVisibility() != BaseConst.ENTRY_VISIBILITY_PRIVATE
					|| getCurrentUserId().equals(getOwnerId())) {
				entityMap.put(entity.getId(), entity);

				// 若访问项权限是limited，需进一步确认用户权限
				if (entity.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
					limitedTaskIDs.add(entity.getId());
				}
			}
		}

		// 检查当前用户对limited访问项的访问权限
		filterLimitedEntities(limitedTaskIDs.toArray(new String[0]), entityMap);

		return getResultsForSpecifiedPage(entityMap.values());
	}

	/**
	 * 返回项的父项，如执行的父项是任务；任务的父项是计划；计划的父项是目标
	 * 
	 * @throws EntityNotFoundException:
	 *             如果没找到指定的项
	 */
	final public BaseEntity getParant(String parentId)
			throws NullObjectException, InvalidQueryException, ServerException,
			InvalidValueException, EntityNotFoundException {
		BaseEntity parent = _dbUtil.getEntityById(_parentClass, parentId);
		if (parent == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error("Parent entity was not found.", e);
			throw e;
		}
		return parent;
	}

	/** 从结果集中摘取指定页面的结果 */
	final protected List getResultsForSpecifiedPage(Collection results) {
		if (results == null || results.isEmpty()) {
			return null;
		}

		// 在剪辑结果集之前，记录结果集的最大数
		_maxResultCount = results.size();
		int page = getPage();
		if (page < 1) {
			page = 1;
		}
		
		// 确定所要结果在结果集中最小和最大位置
		int firstIndex = (page - 1) * BaseConst.MAX_RESULTS_IN_QUERY;
		if (firstIndex > _maxResultCount) {
			page = _maxResultCount / BaseConst.MAX_RESULTS_IN_QUERY;
			if(_maxResultCount % BaseConst.MAX_RESULTS_IN_QUERY != 0) {
				page++;
			}
			
			firstIndex = (page - 1) * BaseConst.MAX_RESULTS_IN_QUERY;
		}
		setPage(page);
		
		int lastIndex = firstIndex + BaseConst.MAX_RESULTS_IN_QUERY;
		if (lastIndex > _maxResultCount) {
			lastIndex = _maxResultCount;
		}
		return trimResultsForSpecifiedPage(results, firstIndex, lastIndex);
	}
	
	private List trimResultsForSpecifiedPage(Collection results, int firstIndex, int lastIndex) {
		// 容器是List，直接调用List的方法取指定结果，提高处理效率
		if (results instanceof List) {
			return ((List) results).subList(firstIndex, lastIndex);
		}
		
		List list = new ArrayList();
		int count = 0;
		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			Object obj = iter.next();
			if (count >= firstIndex && count < lastIndex) {
				list.add(obj);
			}
			count++;
		}
		
		return list;
	}

	/** 删除limited权限记录 */
	final protected void deleteLimitedAccess(String entityId)
			throws NullObjectException, ServerException {
		if (entityId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("Entity id is NULL.", e);
		}
		_dbUtil.executeHQLUpdate(RdcQueryConst.HQL_DELETE_LIMITEDACCESS_BY_ID,
				new Object[] { entityId });

		if (_log.isDebugEnabled()) {
			_log.debug("Successfully deleted limited access records for "
					+ entityId);
		}
	}

	public void cancel(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		throw new ActionNotSupportedException();
	}

	public void complete(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {
		throw new ActionNotSupportedException();
	}

	public List getEntitiesByStatus(int[] states)
			throws ActionNotSupportedException, ServerException,
			InvalidValueException {
		throw new ActionNotSupportedException();
	}
	
	/** 计算entities的评论条数 */
	protected void computeCommentCount(List list) throws ServerException {
		if(list == null || list.isEmpty()) {
			return;
		}
		
		Map<String, BaseEntity> entityMap = new HashMap<String, BaseEntity>();
		Iterator iter = list.iterator();
		while(iter.hasNext()) {
			BaseEntity entity = (BaseEntity) iter.next();
			entityMap.put(entity.getId(), entity);
		}
		
		RdcQuery query = getDbUtil().createNativeSQLQuery(RdcQueryConst.SQL_COMMENT_COUNT_BY_TYPEID);
		query.addScalar("typeId", Hibernate.STRING);
		query.addScalar("amount", Hibernate.INTEGER);
		query.setParameterList("ids", entityMap.keySet().toArray());
		List results = query.getResults();
		if(results == null || results.isEmpty()) {
			return;
		}
		
		iter = results.iterator();
		while(iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			BaseEntity entity = entityMap.get(result[0]);
			if(entity != null) {
				entity.setCommentCount((Integer)result[1]);
			}
		}
	}
}
